home *** CD-ROM | disk | FTP | other *** search
/ The 640 MEG Shareware Studio 2 / The 640 Meg Shareware Studio CD-ROM Volume II (Data Express)(1993).ISO / pascal / ghostbbs.zip / BB1.PAS < prev    next >
Pascal/Delphi Source File  |  1980-01-01  |  35KB  |  1,199 lines

  1. {$R-}
  2. {$C-}
  3. {$U-}
  4. program GSBBS;
  5. const
  6.   version   = 'Version 2.14';
  7.  
  8.   applying = 'BBSINFO\APPLYING.TXT';
  9.   welcome  = 'BBSINFO\WELCOME.TXT';   {Info files:    }
  10.   otherBBS = 'BBSINFO\BBSLIST.TXT';
  11.   helpfile = 'BBSINFO\BBSHELP.TXT';
  12.   sysinfo  = 'BBSINFO\SYSINFO.TXT';
  13.   meetings = 'BBSINFO\MEETING.TXT';
  14.   bulletin = 'BBSINFO\BULLETIN.TXT';
  15.   filehelp = 'BBSINFO\FILEHLP.TXT';
  16.   mainmenu = 'BBSINFO\MAINMENU.TXT';
  17.   readmenu = 'BBSINFO\READMENU.TXT';
  18.   filemenu = 'BBSINFO\FILEMENU.TXT';
  19.   editmenu = 'BBSINFO\EDITMENU.TXT';
  20.  
  21.   sysop   = 5;  { Access levels }
  22.   prefuser= 4;
  23.   expuser = 3;
  24.   reg     = 2;
  25.   newuser = 1;
  26.   twit    = 0;
  27.  
  28.   noecho    = false;
  29.   echo      = true;
  30.   null      = #0;
  31.   abort     = #3;
  32.   bell      = #7;
  33.   bksp      = #8;
  34.   tab       = #9;
  35.   lnfd      = #10;
  36.   cls       = #12;
  37.   cr        = #13;
  38.   pause     = #19;
  39.   esc       = #27;
  40.   space     = ' ';
  41.   maxnumsects  = 20; { maximun number of subboards }
  42.   maxminon  = 60;  {max time in minutes user can be on}
  43.  
  44. type
  45.   regpack = record
  46.     case integer of
  47.       1: (AX,BX,CX,DX,BP,DI,SI,DS,ES,Flags : Integer);
  48.       2: (AL,AH,BL,BH,CL,CH,DL,DH          : Byte);
  49.     end;
  50.  
  51.   str2      = string[2];
  52.   name      = string[14];
  53.   longname  = string[25];
  54.   filbuffer = array[0..127] of byte;
  55.   line      = string[80];
  56.   person    = string[27];
  57.   str10     = string[10];
  58.   long      = string[150];
  59.  
  60.   {$I id.rec }   { id record descripter }
  61.  
  62.   log       = record                    { timelog.bbs record }
  63.                 who  : person;          { User name }
  64.                 rate : string[4];       { baud rate used}
  65.                 msgs : string[2];       { # of msgs posted }
  66.                 when : name;            { log in time }
  67.                 done : name;            { log off time }
  68.               end;
  69.   yesno     = array[boolean] of string[3];
  70.  
  71. const yn: yesno = ('NO','YES');
  72.  
  73. var
  74.   real_name,
  75.   address,
  76.   town_city,
  77.   state_zip   : longname;
  78.   phone_number: name;
  79.   downloads,
  80.   uploads,
  81.   messages_posted,
  82.   logged_on   : integer;
  83.   special_access : byte;
  84.   init_logon : name;
  85.   last_time_on : name;
  86.  
  87.   subboard:   byte;
  88.   libfile:    file;
  89.   libbuff:    filbuffer;
  90.   libeof:     boolean;
  91.   logfile:    file of log;
  92.   logrec:     log;
  93.   idfile:     file of sysid;
  94.   idrec:      sysid;
  95.   caller:     person;
  96.   password,
  97.   timeon,
  98.   timeoff,
  99.   cs,
  100.   message:    name;
  101.   buffer:     long;
  102.   access:     byte;
  103.   libsects,
  104.   usernum,
  105.   lastmess,
  106.   nextmess,
  107.   charcount,
  108.   lastspace,
  109.   bufpointer,
  110.   width:      integer;
  111.   umess:      line;
  112.   upost:      boolean;
  113.   nochat:     integer;
  114.   controls,
  115.   printon,
  116.   local,
  117.   filesopen,
  118.   messopen,
  119.   caps,
  120.   expert:     boolean;
  121.   exitchar, bl, lf, bs     : char;
  122.   min,   onmin,   offmin   : byte;
  123.   hour,  onhour,  offhour  : byte;
  124.   date,  ondate,  offdate  : byte;
  125.   month, onmonth, offmonth : byte;
  126.   year                     : byte;
  127.   usemin, usehour          : integer;
  128.   last_message : array[1..maxnumsects] of integer;
  129.   numsects, numfilesects : byte;
  130.   good_signon: boolean;
  131.   callnum : integer;
  132.   chatsysop : boolean;
  133.   color_mono : string[5];
  134.   sysop_name : person;
  135.   mail_sent, mail_rec, msg_nailed, msg_read : integer;
  136.   today_date,today_year,today_month, today_timeon: byte;
  137.  
  138. { beginning of buffered input routines }
  139.  
  140. const
  141. intseg: INTEGER = 0; {filled with interrupt segment address}
  142. parity_o   = 8;
  143. parity_e   = 24;
  144. parity_n   = 0;
  145. databits_6 = 0;
  146. databits_7 = 2;
  147. databits_8 = 3;
  148. stopbits_1 = 0;
  149. stopbits_2 = 4;
  150.  
  151. save_buf_size = 4096;  {comport input buffer size do not change unless}
  152.                        { you absolutely know what your doing          }
  153. type
  154.   bytepointer = ^byte;
  155.  
  156. VAR
  157. baud,stopbits,databits        : INTEGER;
  158. params, lorate, hirate        : BYTE;
  159. parity                        : CHAR;
  160. parity_                       : INTEGER;
  161. base,comport                  : INTEGER;
  162.  
  163. { interrupt vectors and pointers to them }
  164.  
  165. newvec,
  166. oldvec     : bytepointer;
  167. int4       : bytepointer ABSOLUTE $0000:$0030; {for COM1:}
  168. int3       : bytepointer ABSOLUTE $0000:$002C; {for COM2:}
  169. rcvbuf     : ARRAY[0..4095] OF BYTE;
  170. inptr,
  171. outptr     : INTEGER;
  172. datardy    : boolean;
  173.  
  174. PROCEDURE init_comm_parms;
  175. { called to change comport parms }
  176. BEGIN
  177.     params := 0;
  178.     lorate := LO(TRUNC(115200.0/baud));
  179.     hirate := HI(TRUNC(115200.0/baud));
  180.  
  181.     CASE databits OF                 { Set the number of data bits. }
  182.         6: databits := databits_6;
  183.         7: databits := databits_7;
  184.         8: databits := databits_8;
  185.     END;
  186.  
  187.     CASE stopbits OF                 { Set the # of stop bits. }
  188.         2: stopbits := stopbits_2;
  189.         1: stopbits := stopbits_1;
  190.     END;
  191.  
  192.     parity := UPCASE(parity);        { Convert parity code to upper case.  }
  193.     CASE parity OF                   { Set the parity. }
  194.         'O': parity_ := parity_o;
  195.         'E': parity_ := parity_e;
  196.         'N': parity_ := parity_n;
  197.     END;
  198.     params := databits + stopbits + parity_;
  199. END;
  200.  
  201. FUNCTION inready:boolean;
  202. { determines if there is data at the keyboard or the serial port }
  203. begin
  204.   inready := datardy or keypressed;
  205. end;
  206.  
  207. PROCEDURE setdtr;
  208. BEGIN
  209.     PORT[base+4] := $09; {DTR on and INT enabled}
  210. END;
  211.  
  212. FUNCTION recvchar : char;
  213. { gets char from buffer }
  214. BEGIN
  215.   inline($FA);                        { suspend interrupts }
  216.   if datardy then begin
  217.     recvchar := chr(rcvbuf[outptr]);  { get char and advance buffer head }
  218.     outptr := (outptr + 1) and $fff;
  219.     if inptr = outptr then datardy := false;
  220.   end;
  221.   inline($FB);                        { resume interrupts }
  222. END;
  223.  
  224. {following was pirated from a public domain comm program AND UPDATED for
  225.   a 4k capture buffer }
  226.  
  227. PROCEDURE set_rs232_vector;
  228. PROCEDURE int_handler1;
  229. { This routine buffers all incoming received data for com 1}
  230. BEGIN
  231.     INLINE(
  232.     $50/             {PUSH AX}
  233.     $52/             {PUSH DX}
  234.     $57/             {PUSH DI}
  235.     $1e/             {PUSH DS}          {save registers}
  236.     $2e/             {CS:}
  237.     $8e/$1e/intseg/  {MOV   DS,[Intseg]}        {get data segment pointer}
  238.     $ba/$fd/$03/     {MOV   DX,$3FD}            {is character ready?}
  239.     $ec/             {IN    AL,DX}
  240.     $24/$01/         {AND   AL,01}
  241.     $74/$1a/         {JZ    There:}             { no, skip entry}
  242.                                                 {note: used to be $74/$19/}
  243.                                                 {adjusted for 1 byte expansion}
  244.                                                 {in buffer size}
  245.     $ba/$f8/$03/     {MOV   DX,$3F8}            { yes, get pointer}
  246.     $a1/inptr/       {MOV   AX,[inptr]}         {get index TO buffer}
  247.     $97/             {XCHG  DI,AX}
  248.     $ec/             {IN    AL,DX}              {get data from receiver}
  249.     $88/$85/rcvbuf/  {MOV   [DI+rcvbuf],AL}     {put data into buffer}
  250.     $97/             {XCHG  DI,AX}              {increment pointer}
  251.     $40/             {INC   AX}
  252.     $25/$ff/$0f/     {AND   Ax,$fff}            {note: adjusted for a 4k buffer}
  253.                                                 {used to be $24/$ff/ and al,$ff}
  254.     $a3/inptr/       {MOV   [inptr],AX}
  255.     $b8/$01/$00/     {MOV   AX,1}               {show data is ready}
  256.     $a2/datardy/     {MOV   [datardy],AX}
  257. {There:}
  258.     $b0/$20/         {MOV   AL,$20}             {eoi}
  259.     $e6/$20/         {OUT   $20,AL}
  260.     $1f/             {POP   DS}
  261.     $5f/             {POP   DI}
  262.     $5a/             {POP   DX}
  263.     $58/             {POP   AX}
  264.     $cf);            {IRET}
  265.  
  266. END;
  267.  
  268. PROCEDURE int_handler2;
  269. { This routine buffers all incoming received data for com2}
  270. BEGIN
  271.     INLINE(
  272.     $50/             {PUSH AX}
  273.     $52/             {PUSH DX}
  274.     $57/             {PUSH DI}
  275.     $1e/             {PUSH DS}          {save registers}
  276.     $2e/             {CS:}
  277.     $8e/$1e/intseg/  {MOV   DS,[Intseg]}        {get data segment pointer}
  278.     $ba/$fd/$02/     {MOV   DX,$2FD}            {is character ready?}
  279.     $ec/             {IN    AL,DX}
  280.     $24/$01/         {AND   AL,01}
  281.     $74/$1a/         {JZ    There:}             { no, skip entry}
  282.                                                 {note: used to be $74/$19/}
  283.                                                 {adjusted for 1 byte expansion}
  284.                                                 {in buffer size}
  285.     $ba/$f8/$02/     {MOV   DX,$2F8}            { yes, get pointer}
  286.     $a1/inptr/       {MOV   AX,[inptr]}         {get index TO buffer}
  287.     $97/             {XCHG  DI,AX}
  288.     $ec/             {IN    AL,DX}              {get data from receiver}
  289.     $88/$85/rcvbuf/  {MOV   [DI+rcvbuf],AL}     {put data into buffer}
  290.     $97/             {XCHG  DI,AX}              {increment pointer}
  291.     $40/             {INC   AX}
  292.     $25/$ff/$0f/     {AND   Ax,$fff}            {note: adjusted for a 4k buffer}
  293.                                                 {used to be $24/$ff/ and al,$ff}
  294.     $a3/inptr/       {MOV   [inptr],AX}
  295.     $b8/$01/$00/     {MOV   AX,1}               {show data is ready}
  296.     $a2/datardy/     {MOV   [datardy],AX}
  297. {There:}
  298.     $b0/$20/         {MOV   AL,$20}             {eoi}
  299.     $e6/$20/         {OUT   $20,AL}
  300.     $1f/             {POP   DS}
  301.     $5f/             {POP   DI}
  302.     $5a/             {POP   DX}
  303.     $58/             {POP   AX}
  304.     $cf);            {IRET}
  305.  
  306. END;
  307.  
  308. BEGIN
  309.     intseg := DSeg;
  310.  
  311.     case comport of
  312.       1 : begin
  313.             base   := $3f8;
  314.             oldvec := int4;
  315.             newvec := PTR(CSeg,Ofs(int_handler1)+7+5);
  316.             int4 := newvec;
  317.             INLINE(    $ba/$3f8/             {MOV  DX,BASE}
  318.                        $ec/$ec/$ec/$ec/      {IN   AL,DX}
  319.                        $ba/$3fd/$ec/         {MOV  DX,BASE+5 ! IN  AL,DX}
  320.                        $ba/$3fe/$ec);        {MOV  DX,BASE+6 ! IN  AL,DX}
  321.           end;
  322.       2 : begin
  323.             base   := $2f8;
  324.             oldvec := int3;
  325.             newvec := PTR(CSeg,Ofs(int_handler2)+7+5);
  326.             int3 := newvec;
  327.             INLINE(    $ba/$2f8/             {MOV  DX,BASE}
  328.                        $ec/$ec/$ec/$ec/      {IN   AL,DX}
  329.                        $ba/$2fd/$ec/         {MOV  DX,BASE+5 ! IN  AL,DX}
  330.                        $ba/$2fe/$ec);        {MOV  DX,BASE+6 ! IN  AL,DX}
  331.           end;
  332.     end { case };
  333.  
  334.     datardy := FALSE;   inptr := 0;   outptr := inptr;
  335.  
  336.     case comport of
  337.       1 : {port[$21] := port[$21] and $ef;} { enable interrupts for com1 }
  338.            INLINE($e4/$21/$24/$ef/$e6/$21);
  339.       2 : {port[$21] := port[$21] and $f7;} { enable interrupts for com2 }
  340.            INLINE($e4/$21/$24/$f7/$e6/$21);
  341.     end { case };
  342. END;
  343.  
  344. PROCEDURE setup;
  345. {sets up serial port}
  346. VAR temp : BYTE;
  347. BEGIN
  348.     temp := PORT[base];
  349.     temp := PORT[base+5];
  350.     init_comm_parms;
  351.     PORT[base+4] := $3;
  352.     PORT[base+3] := (params OR hirate OR $80);
  353.     Portw[base]  := TRUNC(115200.0/baud);
  354.     PORT[base+3] := (params OR hirate) AND $7f;
  355.     PORT[base+1] := $01;         {enable receiver interrupts}
  356.     setdtr;                      {put station on-line}
  357. END;
  358.  
  359. {end of buffered input routines }
  360.  
  361. procedure lineout(message: line); forward;
  362.  
  363. function outready: boolean;
  364. {Returns true if serial output port is
  365.  ready to transmit a new character}
  366.   begin
  367.     outready := ((port[base+5] and 32) > 0);
  368.   end;
  369.  
  370. procedure xmitchar(ch: char);
  371. {Transmits ch when serial output port is ready,
  372.    unless we're in the local mode.}
  373.   begin
  374.     if not local then begin
  375.       repeat until outready;
  376.       port[base] := ord(ch);
  377.     end;
  378.   end;
  379.  
  380. function cts: boolean;
  381. {This function returns true if a carrier tone is present on the modem
  382.  and is frequently checked to see if the caller is still present.
  383.  It always returns "true" in the local mode.}
  384.   begin
  385.     cts := ((port[base + 6] and 128) = 128) or local;
  386.   end;
  387.  
  388.  
  389. procedure clearmodem;        (* Modem Dependent *)
  390. {Sets modem for auto-answer, CTS line as carrier detect, no command echo}
  391.   var buffer: line;
  392.       loop  : byte;
  393.       ch    : char;
  394.   begin
  395.     buffer := 'ATS0=1 V0 Q1'; { <- put your initialization string here }
  396.     for loop := 1 to length(buffer) do begin
  397.       ch := buffer[loop];
  398.       xmitchar(ch);
  399.       delay(50);
  400.     end;
  401.     xmitchar(#13);
  402.     writeln;
  403.     write('Delaying...');
  404.     delay(1000); {Delays while modem digests initialization codes}
  405.     writeln;
  406.   end;
  407.  
  408. function badframe: boolean;
  409. {Indicates Framing Error - return false if not available.}
  410.   begin
  411.     badframe := (port[base + 5] and 8) = 8;
  412.   end;
  413.  
  414. procedure dropRTS;
  415. { Lowers RS-232 RTS line - used to inhibit auto-answer
  416.    and to cause modem to hang up }
  417.   begin
  418.     port[base + 4] := 8;
  419.   end;
  420.  
  421. procedure raiseRTS;
  422. (* Raises RTS line to enable auto-answer *)
  423.   begin
  424.     port[base + 4] := 11;
  425.   end;
  426.  
  427. procedure setlocal;
  428. {Sets local flag true and inhibits modem auto-answer}
  429.   begin
  430.     dropRTS; {Inhibits Rixon auto-answer}
  431.     local := true;
  432.   end;
  433.  
  434. procedure clearlocal;
  435. {Clears local flag and allows modem auto-answer}
  436.   begin
  437.     raiseRTS;
  438.     local := false;
  439.   end;
  440.  
  441. procedure dispcaller;
  442. {Displays caller's stats at bottom of host CRT;
  443.  Replace with empty procedure if not desired.}
  444.   begin
  445.     textcolor(yellow);
  446.     window(1,1,80,25);
  447.     clrscr;
  448.     gotoxy(1,1);
  449.     writeln(caller,' #',callnum,' times=',logged_on,' Last=',idrec.lsto,' Start:',init_logon);
  450.     write('Downlds=',downloads,' Uplds=',uploads,' Msgs=',messages_posted);
  451.     write(' Password=',password,' Access=',access,' SpecA=',special_access);
  452.     if chatsysop then textcolor(RED);
  453.     write(' CHAT');
  454.     window(1,3,80,25);
  455.     gotoxy(1,1);
  456.     textcolor(white);
  457.   end;
  458.  
  459. procedure hangup;
  460. {Signals modem to hang up - in this case by lowering RTS line for 500 msec.}
  461.   begin
  462.     if cts then lineout('--- Disconnected ---' + cr + lf);
  463.     dropRTS;
  464.     if local then clearlocal else repeat until not cts;
  465.     raiseRTS;
  466.   end;
  467.  
  468. procedure purgeline;
  469. {gets rid of all chars in the reciver buffer }
  470. var c : char;
  471.   begin
  472.   if not local then
  473.     repeat
  474.       if datardy then c:= recvchar;
  475.     until not datardy;
  476.   end;
  477.  
  478. procedure clock(var year,month,date,hour,min: byte);
  479. {Returns with month in range 1(Jan)..12(Dec),
  480.  date in 1..length of month, hour in 0..23 (24-hr clock)}
  481. { seconds are really not needed and i have chose to ignore them}
  482.   procedure getdate;
  483.   var
  484.     allregs : regpack;
  485.   begin
  486.      allregs.ax := $2A * 256;
  487.      MsDos(allregs);
  488.      month := allregs.dx div 256;
  489.      date := allregs.dx mod 256;
  490.      year := allregs.cx - 1900;
  491.   end;  {getdate}
  492.  
  493.   procedure gettime;
  494.   var
  495.    allregs : regpack;
  496.   begin
  497.      allregs.ax := $2C * 256;
  498.      MsDos(allregs);
  499.      hour := allregs.cx div 256;
  500.      min := allregs.cx mod 256;
  501.   end;  {gettime}
  502.  
  503.   begin
  504.     getdate;
  505.     gettime;
  506.   end;
  507.  
  508. type monthname = string[3];
  509.      monames  = array[1..12] of monthname;
  510.  
  511. const months: monames = ('Jan','Feb','Mar','Apr','May','Jun',
  512.                          'Jul','Aug','Sep','Oct','Nov','Dec');
  513.  
  514. function time(year, month, date, hour, min: byte): name;
  515. {Returns 14-character string containing time and date}
  516.   var
  517.     tempm,
  518.     tempd,
  519.     tempy,
  520.     temph: string[2];
  521.   begin
  522.       str(min:2,tempm);
  523.       str(hour:2,temph);
  524.       str(date:2,tempd);
  525.       str(year:2,tempy);
  526.       if min < 10 then tempm := '0' + tempm[2];
  527.       if date < 10 then tempd := '0' + tempd[2];
  528.       if hour < 10 then temph := '0' + temph[2];
  529.       if year < 10 then tempy := '0' + tempy[2];
  530.       time := temph + ':' + tempm + ' ' + tempd + months[month] + tempy
  531.   end;
  532.  
  533. procedure showtime;
  534.   var
  535.     message: name;
  536.   begin
  537.       clock(year, month, date, hour, min);
  538.       message := time(year, month, date, hour, min);
  539.       lineout('Time is: ' + message);
  540.   end;
  541.  
  542. procedure calcconnect(var usehour, usemin: integer);
  543.   begin
  544.     clock(year, month, date, hour, min);
  545.     usemin := 0;
  546.     usehour := 0;
  547.     usemin := 0;
  548.     usemin := min - onmin + usemin;
  549.     if usemin < 0 then begin
  550.       usemin := usemin + 60;
  551.       usehour := -1;
  552.     end;
  553.     usehour := hour - onhour + usehour;
  554.     if usehour < 0 then usehour := usehour + 24;
  555.   end;
  556.  
  557. procedure connecttime;
  558.   var
  559.     message: name;
  560.   begin
  561.       calcconnect(usehour, usemin);
  562.       message := copy(time(1, 1, 1, usehour, usemin), 1, 5);
  563.       lineout('Connect time: ' + message);
  564.   end;
  565.  
  566. var
  567.   cancelled,canstat : boolean;
  568.   inbuffer  : line;
  569.  
  570. function charin(withecho: boolean):char; forward;
  571.  
  572. procedure sendout(ch: char);
  573. {Character output - bypasses word-wrap; also performs
  574.  "pause" and "abort" input character checks.}
  575.   var temp: char;
  576.       tctl: boolean;
  577.   begin
  578.     if not cancelled
  579.       then begin
  580.       if (inready and canstat)       {canstat=true is used to signal if the}
  581.         then begin                   {output currently being performed can be}
  582.         temp := charin(noecho);      {suspended or cancelled }
  583.         if (temp = pause) then begin
  584.           tctl := controls;
  585.           controls := true;
  586.           temp := charin(noecho);
  587.           controls := tctl;
  588.         end;
  589.         if ((temp = abort) or (temp = space)) then cancelled := true;
  590.       end; { if inready and canstat }
  591.       if not (ch in [lnfd,null])
  592.         then begin
  593.           xmitchar(ch);
  594.           if (ch = cr) then writeln
  595.             else if (ch = bs)
  596.                    then write(bksp)
  597.                    else if ch <> bell then write(ch);
  598.         end
  599.         else if lf <> null then xmitchar(ch);
  600.     end;
  601.   end;
  602.  
  603. procedure flushbuff;
  604. { flushes the word wrap buffer }
  605.   var
  606.     outpointer: byte;
  607.   begin
  608.     if length(buffer) > lastspace then
  609.       for outpointer := lastspace + 1 to length(buffer) do
  610.         sendout(buffer[outpointer]);
  611.     lastspace := length(buffer);
  612.   end;
  613.  
  614. procedure resetbuff;
  615.   begin
  616.     bufpointer := 0;
  617.     lastspace := 0;
  618.     charcount := 0;
  619.     buffer := '';
  620.   end;
  621.  
  622. procedure charout(ch:char);
  623. {Character output using word-wrap}
  624.   var
  625.     buffull   : boolean;
  626.     temp      : long;
  627.   begin
  628.     if caps then ch := upcase(ch);
  629.     if not (ch in [null..#31]) then charcount := succ(charcount);
  630.     if (ch = bs) and (charcount > 0) then charcount := charcount - 1;
  631.     buffer := buffer + ch;
  632.     bufpointer := length(buffer);
  633.     buffull := (charcount + 2 > width);
  634.     if buffull then begin
  635.       if (lastspace > 0)
  636.         then begin
  637.           buffer := copy(buffer, lastspace + 1, bufpointer - lastspace);
  638.           charcount := length(buffer);
  639.           lastspace := 0;
  640.           end {then}
  641.         else begin
  642.           flushbuff;
  643.           resetbuff;
  644.         end; {else}
  645.       sendout(cr);
  646.       sendout(lf);
  647.     end; {if}
  648.     if ch in [null..space] then flushbuff;
  649.     if (ch=cr) then resetbuff;
  650.   end;
  651.  
  652. procedure stringout(message:line);
  653. {outputs a string w/o cr & lf }
  654.   var
  655.     charpos: integer;
  656.   begin
  657.     for charpos := 1 to length(message) do charout(message[charpos]);
  658.   end;
  659.  
  660. procedure lineout; (* "forward" declared in MACHDEP *)
  661. {outputs a string with cr & lf }
  662.   begin
  663.     stringout(message);
  664.     charout(cr);
  665.     charout(lf);
  666.   end;
  667.  
  668. function timedin: boolean;
  669. {returns false if no character received in within
  670.  one second: used for XMODEM and input timeout.}
  671.   var times: integer;
  672.   begin
  673.     times := 0;
  674.     while (times < 500) and not inready do begin
  675.       times := times + 1;
  676.       delay(2);
  677.     end;
  678.     timedin := inready and cts;
  679.   end;
  680.  
  681. { returns 256 + scan code (see appendix in turbo manual) }
  682. FUNCTION get_key:INTEGER;
  683. VAR
  684.   r : regpack;
  685. BEGIN
  686.     r.ax:=0;
  687.     Intr($16,r);
  688.     IF LO(r.ax)=0
  689.     THEN get_key:=HI(r.ax)+256
  690.     ELSE get_key:=LO(r.ax);
  691. END;
  692.  
  693. procedure talk; forward;
  694. procedure savedefaults; forward;
  695.  
  696. function charin;
  697. {gets input chars & masks & performs user timeout checks
  698.  also checks for local function keys }
  699. const
  700.   f1 = 315;   { function key 1 = chat }
  701.   f2 = 316;   { func. key 2 = sysoponly }
  702.   f3 = 317;   { toggles nochat off }
  703.   f4 = 318;   { toggles nochat on }
  704.   f10 = 324;  { function key 10 = log user off }
  705.   var
  706.     ch: char;
  707.     countime, got_key: integer;
  708.   begin
  709.     ch := null;
  710.     countime := 0;
  711.     repeat
  712.       if timedin then ch := recvchar else countime := countime + 1;
  713.       if keypressed
  714.         then begin
  715.           got_key := get_key;
  716.           case got_key of
  717.             f1  :  talk;     { chat }
  718.             f2  :  begin
  719.                      savedefaults;  {saves user stats so they can be edited}
  720.                      if access < 2 then access :=2 else access := access + 1;
  721.                      savedefaults;
  722.                    end;
  723.             f3  :  nochat := 1;
  724.             f4  :  nochat := 2;
  725.             f10 :  begin   { hang up on user }
  726.                      dropRTS;
  727.                      if local then clearlocal else repeat until not cts;
  728.                      raiseRTS;
  729.                    end;
  730.             else if got_key < 256 then ch := chr(got_key)
  731.           end; { case }
  732.         end; { if keypressed }
  733.       if countime > 300 then hangup;  { waits 5 min for input and hangs up if none recvd}
  734.       if not cts then ch := cr;
  735.       if (ch <> bs) and not controls then ch := chr(ord(ch) and 127);
  736.     until (ch in [abort, pause, bs, tab, cr, space..#127])
  737.            or (controls and (ch <> null));
  738.     if (ch = #127) and not controls then ch := bs;
  739.     if withecho
  740.       then begin
  741.         sendout(ch);
  742.         if ch = bs then begin sendout(' '); sendout(bs); end;
  743.       end;
  744.     charin := ch;
  745.   end;
  746.  
  747. function inputstring(withecho: boolean; maxchar: integer): line;
  748.   var
  749.     temp:    line;
  750.     ch:      char;
  751.   begin
  752.     purgeline;
  753.     temp := '';
  754.     repeat
  755.       ch := charin(noecho);
  756.       if (ch = bs)
  757.         then begin
  758.           if length(temp) > 0
  759.             then begin
  760.               temp := copy(temp, 1, length(temp) - 1);
  761.               if withecho
  762.                 then begin
  763.                   sendout(bs);
  764.                   sendout(space);
  765.                   sendout(bs);
  766.                 end;
  767.             end;
  768.         end
  769.         else begin  { if not a backspace }
  770.           if maxchar = 1 then maxchar := 80;
  771.           if (ch <> cr) and (length(temp) < maxchar)
  772.               and ((ch in [tab, space..#126]) or controls)
  773.             then begin
  774.               if ch = tab    { handles tab char }
  775.                 then
  776.                 repeat
  777.                   temp := temp + space;
  778.                   if withecho then sendout(space);
  779.                 until (((length(temp) mod 8) = 0) or (length(temp) >= maxchar))
  780.                 else begin
  781.                   temp := temp + ch;
  782.                   if withecho then sendout(ch);
  783.                 end; {else}  { if tab}
  784.             end
  785.            else if ((ch <> cr) and (maxchar <> 1)) then sendout(bell);
  786.         end;
  787.     until (ch = cr);
  788.     charout(cr); charout(lf);
  789.     inputstring := temp;
  790.   end;
  791.  
  792. function getinput(prompt:line; maxlength:integer; withecho:boolean):line;
  793.   var posn: integer;
  794.       temp: char;
  795.   begin
  796.     if cancelled then begin
  797.       cancelled := false;
  798.       lineout(space);
  799.     end;
  800.     if inbuffer = '' then begin
  801.       repeat
  802.         cancelled := false;
  803.         stringout(prompt);
  804.         if bl = bell then stringout(bl);
  805.       until cancelled = false;
  806.       inbuffer := inputstring(withecho,maxlength);
  807.     end;
  808.     if maxlength = 1 then begin
  809.       if inbuffer = '' then temp := cr else begin
  810.         temp := inbuffer[1];
  811.         inbuffer := copy(inbuffer, 2, length(inbuffer)-1);
  812.         if (length(inbuffer) > 1) and (inbuffer[1] = ';')
  813.           then inbuffer := copy(inbuffer, 2, length(inbuffer)-1);
  814.       end; {else}
  815.       getinput := temp;
  816.     end
  817.     else begin
  818.       posn := pos(';', inbuffer);
  819.       if posn = 0 then posn := length(inbuffer) + 1;
  820.       if posn > maxlength then begin
  821.         posn := maxlength + 1;
  822.         inbuffer := copy(inbuffer, 1, maxlength);
  823.       end;
  824.       getinput := copy(inbuffer, 1, posn - 1);
  825.       if posn >= length(inbuffer)
  826.         then inbuffer := ''
  827.         else inbuffer := copy(inbuffer, posn + 1, length(inbuffer) - posn);
  828.     end;
  829.   end;
  830.  
  831. function allcaps(letters: line): line;
  832. { returns upper case string }
  833.   var
  834.     loop: byte;
  835.     temp: person;
  836.   begin
  837.     temp := '';
  838.     for loop := 1 to length(letters) do
  839.       temp := temp + upcase(letters[loop]);
  840.     allcaps := temp;
  841.   end;
  842.  
  843. procedure userlog; forward;
  844. procedure read_userlog; forward;
  845.  
  846. procedure awaitcall;
  847. { this procedure waits for the incoming call
  848.   or escape = local signon
  849.      f2     = print out timelog
  850.      f3     = print out userlog.}
  851.   const
  852.     f2 = 316;
  853.     f3 = 317;
  854.   var
  855.     junk: char;
  856.     temp : byte;
  857.     got_key : integer;
  858.   begin
  859.     baud := 1200;
  860.     setup;
  861.     write(cr + lf + 'Waiting for call...');
  862.     purgeline;
  863.     repeat
  864.       if keypressed then begin
  865.         got_key := get_key;
  866.         local := got_key = 27;
  867.         case got_key of
  868.           f2 : begin  { if f2 then print out timelog to local terminal}
  869.                  local := true;
  870.                  temp := access;
  871.                  access := sysop;
  872.                  userlog;
  873.                  local := false;
  874.                  access := temp;
  875.                  write(cr + lf + 'Waiting for call...');
  876.                end;
  877.           f3 : begin  { if ^l then printout userlog from local terminal}
  878.                  local := true;
  879.                  read_userlog;
  880.                  local := false;
  881.                  write(cr + lf + 'Waiting for call...');
  882.                end;
  883.         end; { case }
  884.         if local then setlocal else exitchar := chr(got_key);
  885.       end;
  886.     until cts or (exitchar = abort);
  887.     clrscr;
  888.     if exitchar <> abort then begin
  889.       if local then writeln('Local control.') else writeln('On line...');
  890.       delay(500);
  891.       purgeline;
  892.       junk := charin(noecho);
  893.       if badframe or (junk <> cr)
  894.         then begin
  895.           baud := 300;         { if badframe or CR not received then switch to 300}
  896.           setup;
  897.         end;
  898.     end;
  899.   end;
  900.  
  901. procedure clearsc;
  902.   begin
  903.     stringout(cs);  {clears local and remote terminals }
  904.     clrscr;
  905.     delay(500);   {allows time for slow terminal screen clears}
  906.   end;
  907.  
  908. function getcap(prompt: line): char;
  909.   begin
  910.     getcap := upcase(getinput(prompt, 1, echo));
  911.   end;
  912.  
  913. function getint(nmax, star: integer; prompt: line): integer;
  914. {get and integer within a specified range}
  915.   var temp, test: integer;
  916.       outstr, userin: name;
  917.   begin
  918.     str(nmax:4, outstr);
  919.     repeat
  920.       temp := 0;
  921.       userin := getinput(prompt, 4, echo);
  922.       val(userin, temp, test);
  923.       if (test = 0) and (temp > nmax) then lineout('Number too large: ' + outstr + ' maximum.');
  924.     until ((test = 0) and (temp >= 0) and (temp <= nmax))
  925.      or (userin = '*') or (userin = '') or (userin = '?') or not cts;
  926.     if userin = '' then getint := 0
  927.      else if userin = '?' then getint := -1
  928.       else if userin = '*' then getint := star
  929.        else getint := temp;
  930.   end;
  931.  
  932.  
  933. procedure textassign(filename: line; var result:integer);
  934. begin
  935.   assign(libfile,filename);
  936.   {$I-} reset(libfile); {$I+}
  937.   result := ioresult;
  938. end;
  939.  
  940. procedure typefile(fname: line; nowrap: boolean);
  941. {output a text file}
  942. var in_ptr,
  943.     result  : integer;
  944.     in_buff : filbuffer;
  945.     eofin   : boolean;
  946.     c       : char;
  947.  
  948. procedure libread(var fileblock:filbuffer);
  949. var ercode:integer;
  950. begin
  951.   {$I-} blockread(libfile,fileblock,1); {$I+}
  952.   ercode := ioresult;
  953.   eofin := (ercode <> 0);
  954. end;
  955.  
  956. function getch:integer;
  957. begin
  958.   in_ptr := in_ptr + 1;
  959.   if in_ptr > 127 then
  960.     begin
  961.       libread(in_buff);
  962.       in_ptr := 0;
  963.     end;
  964.   getch := in_buff[in_ptr];
  965. end;
  966.  
  967.   begin
  968.     canstat := true;
  969.     eofin := false;
  970.     in_ptr := -1;
  971.     textassign(fname, result);
  972.     if result <> 0 then lineout('Cant find ' + fname + ' !')
  973.     else begin
  974.       libread(in_buff);
  975.       while cts and not(cancelled or eofin) do
  976.         begin
  977.           c:=chr(getch);
  978.           if c = #26 then eofin := true
  979.             else begin
  980.               if nowrap
  981.                 then begin
  982.                       if (c <> lnfd) then charout(c);
  983.                       if c = cr then charout(lf);
  984.                 end
  985.                 else sendout(c);
  986.             end;
  987.         end;
  988.         close(libfile);
  989.     end;
  990.   canstat := false;
  991. end;
  992.  
  993. procedure outfile(fname: line);
  994. {output a text file with wordwrap}
  995.   begin
  996.     typefile(fname, true);
  997.   end;
  998.  
  999. function itoa(x:integer):str10;
  1000. { just a way of converting the str procedure into a more convenient function}
  1001. var temp : str10;
  1002. begin
  1003.    temp := '';
  1004.    str(x,temp);
  1005.    itoa := temp;
  1006. end;
  1007.  
  1008. function ftoa(x:real; places:integer):str10;
  1009. { just a way of converting the str procedure into a more convenient function}
  1010. var temp : str10;
  1011. begin
  1012.    temp := '';
  1013.    str(x:places,temp);
  1014.    ftoa := temp;
  1015. end;
  1016.  
  1017. function nextuser: integer;
  1018. { finds the next empty position in the IDS.BBS file }
  1019.   var temp: integer;
  1020.       found:boolean;
  1021.   begin
  1022.     temp := -1;
  1023.     found := false;
  1024.     assign(idfile,'IDS.BBS');
  1025.     reset(idfile);
  1026.     while ((found = false) and not eof(idfile)) do
  1027.       begin
  1028.         temp := temp + 1;
  1029.         read(idfile,idrec);
  1030.         if idrec.pass = '***' then found := true;
  1031.       end;
  1032.     if found = false then if filesize(idfile) > 0
  1033.                              then nextuser := filesize(idfile)
  1034.                              else nextuser := 0
  1035.                        else nextuser := temp;
  1036.   close(idfile);
  1037.   end;
  1038.  
  1039. procedure save_config;
  1040. { setup file, more stuff could go in here, its up to you -
  1041.   right now just the total number of callers, the sysops name,
  1042.   and color or mono crt are defined externally here }
  1043. var callfile : text;
  1044. begin
  1045.   assign(callfile,'config.cnf');
  1046.   rewrite(callfile);
  1047.   writeln(callfile,callnum);
  1048.   writeln(callfile,sysop_name);
  1049.   writeln(callfile,color_mono);
  1050.   writeln(callfile,numsects);
  1051.   writeln(callfile,numfilesects);
  1052.   close(callfile);
  1053. end;
  1054.  
  1055. procedure get_config;
  1056. var callfile : text;
  1057. begin
  1058.   assign(callfile,'config.cnf');
  1059.   reset(callfile);
  1060.   readln(callfile,callnum);
  1061.   readln(callfile,sysop_name);
  1062.   readln(callfile,color_mono);
  1063.   readln(callfile,numsects);
  1064.   readln(callfile,numfilesects);
  1065.   close(callfile);
  1066. end;
  1067.  
  1068. Procedure update_userlog(updatestr:long);
  1069. { used to update userlog. just call this proc with the string you
  1070.   want put into the userlog.bbs file }
  1071. var
  1072.   userlogfile : text;
  1073.   errcode : integer;
  1074.  
  1075. begin
  1076.   assign(userlogfile,'userlog.txt');
  1077.   {$I-}
  1078.   reset(userlogfile);
  1079.   {$I+}
  1080.   errcode := ioresult;
  1081.   if errcode <> 0 then close(userlogfile);
  1082.   if errcode = 0 then append(userlogfile)
  1083.                  else rewrite(userlogfile);
  1084.   writeln(userlogfile,updatestr);
  1085.   close(userlogfile);
  1086. end;
  1087.  
  1088. procedure read_userlog;
  1089. {reads the userlog.bbs file }
  1090. var ch : char;
  1091.     junk : file;
  1092. begin
  1093.   clearsc;
  1094.   outfile('userlog.txt');
  1095.   ch := getcap(' Kill (Y/N) ? ');
  1096.   if ch = 'Y' then
  1097.     begin
  1098.       assign(junk,'userlog.txt');
  1099.       {$I-}
  1100.       erase(junk);
  1101.       {$I+}
  1102.       if ioresult <> 0 then lineout('Already Been Erased! ');
  1103.     end;
  1104. end;
  1105.  
  1106. procedure savedefaults;
  1107. { finish updating userlog.bbs and save the callers stats }
  1108.   var i : byte;
  1109.   begin
  1110.   update_userlog('Msgs Nailed : ' + itoa(msg_nailed) + cr + lf +
  1111.                  'Msgs Read   : ' + itoa(msg_read) + cr + lf +
  1112.                  'Mail Recvd  : ' + itoa(mail_rec) + cr + lf +
  1113.                  'Mail Sent   : ' + itoa(mail_sent));
  1114.   clock(year, month, date, hour, min);
  1115.   update_userlog('Time signed off : ' +  time(year, month, date, hour, min));
  1116.   calcconnect(usehour, usemin);
  1117.   update_userlog('Connect time : ' + copy(time(1, 1, 1, usehour, usemin), 1, 5));
  1118.     with idrec do begin
  1119.       save_config;
  1120.       logged_on := logged_on + 1;
  1121.       user := caller;
  1122.       if expert then exfl := 0 else exfl := 255;
  1123.       lsto := timeon;
  1124.       for i := 1 to numsects do
  1125.         lstm[i] := last_message[i];
  1126.       pass := password;
  1127.       user := caller;
  1128.       user2 := real_name;
  1129.       addr := address;
  1130.       city := town_city;
  1131.       szip := state_zip;
  1132.       phnn := phone_number;
  1133.       dld := downloads;
  1134.       uld := uploads;
  1135.       mptd := messages_posted;
  1136.       lgdn := logged_on;
  1137.       speca := special_access;
  1138.       intlg := init_logon;
  1139.  
  1140.       clock(year, month, date, hour, min);
  1141.       lsto := time(year, month, date, hour, min);
  1142.       tdt := date;
  1143.       tmo := month;
  1144.       tyr := year;
  1145.       tto := usemin + usehour * 60 + today_timeon;
  1146.  
  1147.       clr := cs;
  1148.       acc := access;
  1149.       bsp := bs;
  1150.       lnf := lf;
  1151.       upc := caps;
  1152.       wid := width;
  1153.     end;
  1154.     assign(idfile,'IDS.BBS');
  1155.     reset(idfile);
  1156.     seek(idfile, usernum);
  1157.     write(idfile, idrec);
  1158.     close(idfile);
  1159.   end;
  1160.  
  1161. procedure disconnect;
  1162. { byby procedure }
  1163.   var
  1164.     ch: char;
  1165.   begin
  1166.     clearsc;
  1167.     ch := getcap('DEMATERIALIZE ? Are you SURE? (Y/N)? ');
  1168.     if ch = 'Y'
  1169.       then begin
  1170.         connecttime;
  1171.         lineout('You were caller number '+ itoa(callnum));
  1172.         lineout('Thanks for calling, ' + caller);
  1173.         hangup;
  1174.       end;
  1175.   end;
  1176.  
  1177. Procedure SetBorderColor(ColorNumber : byte);
  1178. {sets border color, i dont know if this works on an IBM mono screen
  1179.   so you can turn it off by putting 'MONO' in the CONFIG.BBS file }
  1180. Begin
  1181. if color_mono = 'COLOR'
  1182.   then begin
  1183.    Inline
  1184.       ($50/                 { PUSH AX             ; save registers         }
  1185.        $52/                 { PUSH DX             ;   "      "             }
  1186.        $8A/$86/ColorNumber/ { MOV  AL,[BP + ColorNumber]                   }
  1187.        $BA/$D9/$03/         { MOV  DX,03D9H       ; portaddress of 6845 CRT
  1188.                                                     color-select register  }
  1189.        $EE/                 { OUT  DX,AL          ; send the color code    }
  1190.        $5A/                 { POP  DX             ; restore registers      }
  1191.        $58)                 { POP  AX             ;   "         "          }
  1192.   end;
  1193. End;
  1194.  
  1195. {$I beep.inc }
  1196. {$I bb2.pas }
  1197. {$I bb3.pas }
  1198. { Turbo Needs something here or you get an unexpected end of file error }
  1199.